package com.ys.utils.yinsheng.yztdock.util;

import com.ys.utils.yinsheng.yztdock.common.Consts;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


public class SignUtil {

	public static final String ALLCHAR = "0123456789ABCDEF";
	private static Logger logger = LoggerFactory.getLogger(SignUtil.class);

	public  final static Map<String, Object> certMap = new ConcurrentHashMap<String, Object>();
	public  final static Map<String, Object> privateMap = new ConcurrentHashMap<String, Object>();

	// 生成随机密钥
	public static String getRandomKey() {
		StringBuilder sb = new StringBuilder();
		Random random = new Random();
		for (int i = 0; i < 16; i++) {
			sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length())));
		}
		String key = ByteUtil.toHexString(sb.toString());
		return key;
	}

	// AES对称加密请求参数
	public static String encryptMessage(String msgStr, String key) {
		byte[] bte;
		String msg = null;
		try {
			bte = AESUtil.encrypt(msgStr.getBytes("UTF-8"), ByteUtil.hexStringToBytes(key));
			msg = Base64.encodeBase64String(bte);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return msg;
	}

	// 对随机密钥进行加密
	public static String encryptKey(String key, PublicKey publicKey) {
		byte[] byte1;
		String check = null;
		try {
			byte1 = RSAUtil.encrypt(ByteUtil.hexStringToBytes(key),publicKey);
			check = Base64.encodeBase64String(byte1);

		} catch (Exception e) {
			e.printStackTrace();
		}
		return check;
	}

	public static Map<String, String> getRequestParameter(String src, String apiName, String msg, String check, String certId, PrivateKey privateKey) {
		Map<String, String> map = new HashMap<String, String>();
		map.put("src", "02");
		map.put("msgCode", apiName);
		map.put("sendTo", "qrcode");
		map.put("timeStamp", DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:dd"));
		map.put("msg", msg);
		map.put("check", check);
		map.put("norce", UUID.randomUUID().toString().replaceAll("-", ""));
		// 加密证书id  即商户登录号
		map.put("certId", certId);
		map.put("version","1");

		// 组装待签名字符串
		List<String> keys = new ArrayList<>(map.keySet());
		Collections.sort(keys);
		StringBuilder sb1 = new StringBuilder();
		for (String k : keys) {
			if ("sign".equals(k)) {
				continue;
			}
			sb1.append(k).append("=");
			sb1.append(map.get(k));
			sb1.append("&");
		}
		if (sb1.length() > 0) {
			sb1.setLength(sb1.length() - 1);
		}
		logger.info("待签名数据: { " + sb1.toString() + " }");

		// 签名
		map.put("sign", RSAUtil.sign(sb1.toString(), "UTF-8",privateKey));

		return map;
	}

	// 验证签名并解密
	public static String decryptMessage(Map<String,String>msgMap,String check,PublicKey publicKey) throws Exception {
		String certId = "s666";
		String data = null;
		if (!RSAUtil.validateSign(Base64.decodeBase64(msgMap.get("sign")),
				SignUtil.getSignStr(msgMap).getBytes(Consts.CHARSET_CODE), certId,publicKey)) {
			throw new Exception("签名验证失败！");
		}

		logger.info("签名验证成功！");

		try {
			
			if (!StringUtils.isEmpty(msgMap.get("data"))) {
				 byte[] byteMsg = AESUtil.decrypt(Base64.decodeBase64(msgMap.get("data")), ByteUtil.hexStringToBytes(check));
				data = org.apache.commons.codec.binary.StringUtils.newStringUtf8(byteMsg);
			}
		} catch (Exception e) {
			throw new Exception("请求失败，非授权请求");
		}
		return data;
	}

	public static String getSignStr(Map<String, String> map) {

		List<String> keys = new ArrayList<String>(map.keySet());
		Collections.sort(keys);
		StringBuilder sb = new StringBuilder();
		for (String key : keys) {
			if ("sign".equals(key)) {
				continue;
			}
			sb.append(key).append("=");
			sb.append(map.get(key));
			sb.append("&");
		}
		if (sb.length() > 0) {
			sb.setLength(sb.length() - 1);
		}
		return sb.toString();
	}

	/**
	 * 除去数组中的空值和签名参数
	 *
	 * @param sArray
	 *            签名参数组
	 * @return 去掉空值与签名参数后的新签名参数组
	 */
	public static Map<String, String> paraFilter(Map<String, String> sArray) {

		Map<String, String> result = new HashMap<String, String>();

		if (sArray == null || sArray.size() <= 0) {
			return result;
		}

		for (String key : sArray.keySet()) {
			String value = sArray.get(key);
			if (value == null || StringUtils.isEmpty(value) || key.equalsIgnoreCase("sign")) {
				continue;
			}
			result.put(key, value);
		}

		return result;
	}

	/**
	 * 遍历以及根据重新排序
	 *
	 * @param sortedParams
	 * @return
	 */
	public static String getSignContent(Map<String, String> sortedParams) {
		StringBuffer content = new StringBuffer();
		List<String> keys = new ArrayList<String>(sortedParams.keySet());
		Collections.sort(keys);
		int index = 0;
		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = sortedParams.get(key);
			if (StringUtils.areNotEmpty(key, value)) {
				content.append((index == 0 ? "" : "&") + key + "=" + value);
				index++;
			}
		}
		return content.toString();
	}

	/**
	 * 签名
	 *
	 * @param content
	 * @param charset
	 * @param pfxCertFileInputStream
	 * @return
	 * @throws Exception
	 */
	public static String rsaSign(String content, String charset, InputStream pfxCertFileInputStream) throws Exception {
		try {
			System.out.println("进入签名方法：content[" + content + "], charset[" + charset + "]");

			PrivateKey priKey = getPrivateKeyFromPKCS12("3ZAa*RiiiI3P&!&U", pfxCertFileInputStream);

			java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");

			signature.initSign(priKey);

			if (StringUtils.isEmpty(charset)) {
				signature.update(content.getBytes());
			} else {
				signature.update(content.getBytes(charset));
			}

			byte[] signed = signature.sign();

			String sign = new String(Base64.encodeBase64(signed), charset);

			System.out.println("进入签名完：content[" + content + "], charset[" + charset + "], sign[" + sign + "]");

			return sign;
		} catch (Exception e) {
			System.out.println("签名失败：content[" + content + "], charset[" + charset + "]");
			throw new Exception("RSAcontent = " + content + "; charset = " + charset, e);
		}
	}

	/**
	 * 读取PKCS12格式的key（私钥）pfx格式
	 *
	 * @param password
	 * @param ins
	 * @return
	 * @throws Exception
	 * @see
	 */
	public static PrivateKey getPrivateKeyFromPKCS12(String password, InputStream ins) throws Exception {
		PrivateKey priKey = (PrivateKey) privateMap.get("PrivateKey");
		if (priKey != null) {
			return priKey;
		}

		KeyStore keystoreCA = KeyStore.getInstance("PKCS12");
		try {
			// 读取CA根证书
			keystoreCA.load(ins, password.toCharArray());

			Enumeration<?> aliases = keystoreCA.aliases();
			String keyAlias = null;
			if (aliases != null) {
				while (aliases.hasMoreElements()) {
					keyAlias = (String) aliases.nextElement();
					// 获取CA私钥
					priKey = (PrivateKey) (keystoreCA.getKey(keyAlias, password.toCharArray()));
					if (priKey != null) {
						privateMap.put("PrivateKey", priKey);
						break;
					}
				}
			}
		} catch (Exception e) {
			if (ins != null)
				ins.close();
			throw e;
		} finally {
			if (ins != null) {
				ins.close();
			}
		}

		return priKey;
	}
	/**
	 * 把参数签名
	 *
	 * @param params
	 * @param charset
	 * @param pfxCertFile
	 * @return
	 * @throws Exception
	 */
	public static String rsaSign(Map<String, String> params, String charset, String pfxCertFile){
		String signContent = getSignContent(params);
		String sign = "";
		InputStream is = null;
		try {
			is = new FileInputStream(pfxCertFile);
			sign = rsaSign(signContent, charset, is);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return sign;
	}

	/**
	 * 验签
	 *
	 * @param publicCertFileInputStream
	 * @param content
	 * @param sign
	 * @param charset
	 * @return
	 * @throws Exception
	 */
	public static boolean rsaCheckContent(InputStream publicCertFileInputStream, String content, String sign,
										  String charset) throws Exception {
		System.out.println("进入验证签名方法: content[" + content + "], sign[" + sign + "], charset[" + charset + "]");
		boolean bFlag = false;
		try {
			java.security.Signature signetcheck = java.security.Signature.getInstance("SHA1WithRSA");
			signetcheck.initVerify(getPublicKeyFromCert(publicCertFileInputStream));
			signetcheck.update(content.getBytes(charset));
			if (signetcheck.verify(Base64.decodeBase64(sign.getBytes(charset)))) {
				// 跑不进条件语句里面
				bFlag = true;
				System.out.println("解密成功");
				System.out.println("sign:" + Base64.decodeBase64(sign.getBytes(charset)).toString());
			}
			System.out.println("进入验证签名方法: content[" + content + "], sign[" + sign + "], charset[" + charset
					+ "], result[" + bFlag + "]");
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("验证签名异常" + ": content[" + content + "], sign[" + sign + "], charset[" + charset + "]");
			throw new Exception("验证签名异常");
		}

		return bFlag;
	}

	/**
	 * 读取公钥，x509格式
	 *
	 * @param ins
	 * @return
	 * @throws Exception
	 * @see
	 */
	public static PublicKey getPublicKeyFromCert(InputStream ins) throws Exception {
		PublicKey pubKey = (PublicKey) certMap.get("PublicKey");
		if (pubKey != null) {
			return pubKey;
		}

		try {
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			Certificate cac = cf.generateCertificate(ins);
			pubKey = cac.getPublicKey();
			certMap.put("PublicKey", pubKey);
		} catch (Exception e) {
			if (ins != null)
				ins.close();
			throw e;
		} finally {
			if (ins != null) {
				ins.close();
			}
		}

		return pubKey;
	}
}
